package com.handa.documentManager.service.impl;

import com.handa.common.exception.ServiceException;
import com.handa.documentManager.entity.DTO.FileObject;
import com.handa.documentManager.entity.VO.FileDownloadResult;
import com.handa.documentManager.entity.VO.MinioFileInfoVO;
import com.handa.documentManager.entity.VO.UploadResult;
import io.minio.*;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.MinioException;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.handa.documentManager.utils.FileDownLoadUtil.downloadFile;
import static com.handa.documentManager.utils.FileDownLoadUtil.getFileNameFromUrl;


@Service
@Slf4j
public class MinIOFileService {

    @Resource
    private MinioClient minioClient;

    /**
     * 获取所有的BucketName
     */
    public List<String> getAllBucket() {
        List<String> res = new ArrayList<>();
        try {
            List<Bucket> buckets = minioClient.listBuckets();
            for (Bucket bucket : buckets) {
                res.add(bucket.name());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return res;
    }

    /**
     * 获取文件流
     */
    @SneakyThrows(Exception.class)
    public InputStream getObject(String bucketName, String objectName) {
        return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 获取文件信息, 如果抛出异常则说明文件不存在
     *
     * @param bucketName 存储桶
     * @param objectName 文件名称
     */
    @SneakyThrows(Exception.class)
    public String getFileContentType(String bucketName, String objectName) {
        return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()).contentType();
    }


    @SneakyThrows(Exception.class)
    public String getPresignedObjectUrl(String bucketName, String objectName) {
        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs
                .builder()
                .bucket(bucketName)
                .object(objectName)
                .method(Method.GET)
                .expiry(7, TimeUnit.DAYS)  // 链接有效期
                .build();
        return minioClient.getPresignedObjectUrl(args);
    }

    public void newFolder(String folderName, String bucketName) throws Exception {
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(folderName)
                        .stream(new ByteArrayInputStream(new byte[]{}), 0, -1)
                        .build()
        );
    }


    /**
     * 上传文件 到指定 bucketName
     */
    public boolean uploadFile(String bucketName, MultipartFile file, String objectName) {
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName) // 存储桶
                    .object(objectName) // 文件名
                    .stream(file.getInputStream(), file.getSize(), -1) // 文件内容
                    .contentType(file.getContentType()) // 文件类型
                    .build());
            return true;
        } catch (Exception e) {
            return false;
        }

    }

    /**
     * 上传文件到MinIO
     *
     * @param objectName  对象名称
     * @param inputStream 文件输入流
     * @param size        文件大小
     * @param contentType 文件类型
     * @return 上传结果
     */
    public String uploadFileBytesToBucket(String bucket, String objectName, InputStream inputStream, long size, String contentType)
            throws Exception {

        // 确保桶存在
        existBucket(bucket);

        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucket)
                        .object(objectName)
                        .stream(inputStream, size, -1)
                        .contentType(contentType)
                        .build()
        );

        return objectName;
    }

    /**
     * 从URL下载文件并上传到MinIO
     */
    public UploadResult uploadToMinioFromUrl(String bucketName, String fileUrl, String customObjectName)
            throws Exception {

        // 下载文件
        FileDownloadResult downloadResult = downloadFile(fileUrl);

        // 确定对象名称
        String objectName = StringUtils.isNotBlank(customObjectName) ? customObjectName :
                getFileNameFromUrl(fileUrl);

        byte[] data = downloadResult.getData();
        String uploadedObjectName = "";
        String contentType = downloadResult.getContentType();
        try (InputStream inputStream = new ByteArrayInputStream(data)) {
            uploadedObjectName = uploadFileBytesToBucket(bucketName, objectName, inputStream, data.length, contentType);
        }

        // 获取访问URL
        String accessUrl = getPresignedObjectUrl(bucketName, objectName);
        ;

        return new UploadResult(uploadedObjectName, accessUrl, downloadResult.getSize());
    }


    /**
     * 删除文件
     */
    public boolean delete(String bucketName, String path) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(bucketName) // 存储桶
                    .object(path) // 文件名
                    .build());
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 删除给定前缀的所有对象。
     */
    public void deleteFolder(String bucketName, String folderPrefix) throws IOException, NoSuchAlgorithmException, InvalidKeyException, MinioException {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(folderPrefix).recursive(true).build());

        for (Result<Item> result : results) {
            Item item = result.get();
            // 删除每个对象
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(item.objectName())
                            .build());
        }
    }

    public boolean batchDelete(String bucketName, List<String> path) {
        try {
            List<DeleteObject> dos = path.stream().map(DeleteObject::new).collect(Collectors.toList());
            Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());
            //必须遍历了迭代器才能删除成功
            for (Result<DeleteError> result : results) {
                DeleteError error = result.get();
                log.error("minio批量删除文件失败：" + error.objectName() + "; " + error.message());
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    /**
     * 在指定文件桶内 创建文件
     */
    public void newFileAndUploadFileToMinio(String bucket, String fileName, String fileType) throws Exception {
        // 创建临时文件
        File tempFile = File.createTempFile(fileName, fileType);

        // 上传文件到MinIO
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucket)
                        .object(fileName + "." + fileType)
                        .stream(new FileInputStream(tempFile), tempFile.length(), -1)
                        .contentType("text/plain")
                        .build());

        // 删除临时文件
        tempFile.delete();
    }

    /**
     * 移动MinIO中的文件到另一个目录
     *
     * @param sourceObject    源文件路径（包含文件名）
     * @param targetDirectory 目标目录路径
     * @return 移动成功返回true，否则返回false
     */
    public boolean moveFileInMinio(String bucketName, String sourceObject, String targetDirectory) {
        try {
            // 检查源文件是否存在
            if (!objectExists(bucketName, sourceObject)) {
                log.error("源文件不存在: {}", sourceObject);
                return false;
            }

            // 确保目标目录格式正确（以/结尾）
            if (!targetDirectory.endsWith("/")) {
                targetDirectory += "/";
            }

            // 从源路径中提取文件名
            String fileName = extractFileName(sourceObject);

            // 构建目标文件路径
            String targetObject = targetDirectory + fileName;

            // 复制文件到新位置
            copyObject(bucketName, sourceObject, targetObject);

            // 删除原文件
            deleteObject(bucketName, sourceObject);

            log.info("MinIO文件移动成功: {} -> {}", sourceObject, targetObject);
            return true;

        } catch (Exception e) {
            log.error("MinIO文件移动失败: {}", e.getMessage(), e);
            return false;
        }
    }


    /**
     * 完整的文件夹转移（包括文件夹本身）
     *
     * @param sourceFolder       源文件夹名称（如：folder1）
     * @param targetParentFolder 目标父文件夹路径（如：parent/ 或空字符串表示根目录）
     * @param newFolderName      新文件夹名称（如：folder2，如果为null则保持原名称）
     * @return 转移成功返回true，否则返回false
     */
    public boolean transferFolder(String bucketName, String sourceFolder, String targetParentFolder, String newFolderName) {
        try {
            // 规范化路径
            String sourceFolderPath = normalizeFolderPath(sourceFolder, false);
            String targetParentPath = normalizeFolderPath(targetParentFolder, true);

            // 确定目标文件夹名称
            String finalFolderName = (newFolderName != null && !newFolderName.trim().isEmpty()) ?
                    newFolderName : extractFolderName(sourceFolderPath);

            String targetFolderPath = targetParentPath + finalFolderName + "/";

            log.info("开始转移文件夹: {} -> {}", sourceFolderPath, targetFolderPath);

            // 检查源文件夹是否存在
            if (!folderExists(bucketName, sourceFolderPath)) {
                log.error("源文件夹不存在: {}", sourceFolderPath);
                return false;
            }

            // 检查目标位置是否冲突
            if (folderExists(bucketName, targetFolderPath)) {
                log.error("目标文件夹已存在: {}", targetFolderPath);
                return false;
            }

            // 获取源文件夹中的所有内容（包括子文件夹和文件）
            List<MinioObject> allObjects = listAllObjectsInFolder(bucketName, sourceFolderPath, true);

            if (allObjects.isEmpty()) {
                log.warn("源文件夹为空，创建空目标文件夹");
                // 创建空文件夹标记
                createEmptyFolder(bucketName, targetFolderPath);
                return true;
            }

            log.info("找到 {} 个对象需要转移", allObjects.size());

            // 转移所有对象
            boolean allSuccess = true;
            int successCount = 0;

            for (MinioObject minioObject : allObjects) {
                String sourceObject = minioObject.getObjectName();
                String targetObject = sourceObject.replace(sourceFolderPath, targetFolderPath);

                boolean success = transferSingleObject(bucketName, sourceObject, targetObject);
                if (success) {
                    successCount++;
                } else {
                    allSuccess = false;
                    log.error("转移对象失败: {} -> {}", sourceObject, targetObject);
                }
            }

            if (allSuccess) {
                log.info("文件夹转移成功: 共转移 {} 个对象", successCount);

                // 删除源文件夹（如果为空）
                deleteFolderIfEmpty(bucketName, sourceFolderPath);

            } else {
                log.warn("文件夹部分转移成功: 成功 {} 个，失败 {} 个",
                        successCount, allObjects.size() - successCount);
            }

            return allSuccess;

        } catch (Exception e) {
            log.error("文件夹转移失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 从路径中提取文件夹名称
     */
    private String extractFolderName(String folderPath) {
        folderPath = folderPath.replaceAll("/+$", ""); // 移除末尾的斜杠
        int lastSlashIndex = folderPath.lastIndexOf('/');
        if (lastSlashIndex >= 0) {
            return folderPath.substring(lastSlashIndex + 1);
        }
        return folderPath;
    }

    /**
     * 确保目标对象的父目录存在
     */
    private void ensureParentDirectoriesExist(String bucketName, String objectPath) throws Exception {
        int lastSlashIndex = objectPath.lastIndexOf('/');
        if (lastSlashIndex > 0) {
            String parentPath = objectPath.substring(0, lastSlashIndex + 1);
            if (!folderExists(bucketName, parentPath)) {
                createEmptyFolder(bucketName, parentPath);
            }
        }
    }

    /**
     * 创建空文件夹标记
     */
    private void createEmptyFolder(String bucketName, String folderPath) throws Exception {
        // 创建空文件作为文件夹标记
        String markerFile = folderPath + ".folder-marker";
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(markerFile)
                        .stream(new ByteArrayInputStream(new byte[0]), 0, -1)
                        .build());

        log.debug("创建空文件夹标记: {}", markerFile);
    }

    /**
     * 如果文件夹为空则删除
     */
    private void deleteFolderIfEmpty(String bucketName, String folderPath) throws Exception {
        List<MinioObject> remainingObjects = listAllObjectsInFolder(bucketName, folderPath, false);
        if (remainingObjects.isEmpty()) {
            // 删除文件夹标记文件（如果有）
            String markerFile = folderPath + ".folder-marker";
            if (objectExists(bucketName, markerFile)) {
                deleteObject(bucketName, markerFile);
            }
            log.info("删除空源文件夹: {}", folderPath);
        }
    }

    /**
     * 转移单个对象
     */
    private boolean transferSingleObject(String bucketName, String sourceObject, String targetObject) {
        try {
            // 检查源对象是否存在
            if (!objectExists(bucketName, sourceObject)) {
                log.error("源对象不存在: {}", sourceObject);
                return false;
            }

            // 确保目标目录存在（创建必要的父目录）
            ensureParentDirectoriesExist(bucketName, targetObject);

            // 复制对象到新位置
            minioClient.copyObject(
                    CopyObjectArgs.builder()
                            .bucket(bucketName)
                            .object(targetObject)
                            .source(
                                    CopySource.builder()
                                            .bucket(bucketName)
                                            .object(sourceObject)
                                            .build())
                            .build());

            // 删除原对象
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(sourceObject)
                            .build());

            log.debug("对象转移成功: {} -> {}", sourceObject, targetObject);
            return true;

        } catch (MinioException e) {
            log.error("MinIO操作失败: {} -> {}: {}", sourceObject, targetObject, e.getMessage());
            return false;
        } catch (Exception e) {
            log.error("转移对象失败: {} -> {}: {}", sourceObject, targetObject, e.getMessage());
            return false;
        }
    }


    /**
     * 获取文件夹中的所有对象
     */
    private List<MinioObject> listAllObjectsInFolder(String bucketName, String folderPath, boolean includeSubfolders) throws Exception {
        List<MinioObject> objects = new ArrayList<>();

        ListObjectsArgs args = ListObjectsArgs.builder()
                .bucket(bucketName)
                .prefix(folderPath)
                .recursive(includeSubfolders)
                .build();

        Iterable<Result<Item>> results = minioClient.listObjects(args);
        for (Result<Item> result : results) {
            try {
                Item item = result.get();
                // 排除文件夹标记文件
                if (!item.objectName().endsWith(".folder-marker")) {
                    MinioObject minioObject = new MinioObject();
                    minioObject.setObjectName(item.objectName());
                    minioObject.setSize(item.size());
                    minioObject.setLastModified(item.lastModified().toLocalDate());
                    minioObject.setDirectory(item.isDir());
                    objects.add(minioObject);
                }
            } catch (Exception e) {
                log.warn("处理MinIO对象时出错: {}", e.getMessage());
            }
        }

        return objects;
    }

    /**
     * 规范化文件夹路径
     */
    private String normalizeFolderPath(String path, boolean isParentPath) {
        if (path == null) {
            return "";
        }

        path = path.trim();

        // 移除开头的斜杠
        if (path.startsWith("/")) {
            path = path.substring(1);
        }

        // 对于父路径，确保以斜杠结尾
        if (isParentPath && !path.isEmpty() && !path.endsWith("/")) {
            path = path + "/";
        }

        return path;
    }

    /**
     * 复制对象
     */
    private void copyObject(String bucketName, String sourceObject, String targetObject) throws Exception {
        minioClient.copyObject(
                CopyObjectArgs.builder()
                        .bucket(bucketName)
                        .object(targetObject)
                        .source(
                                CopySource.builder()
                                        .bucket(bucketName)
                                        .object(sourceObject)
                                        .build())
                        .build());
    }

    /**
     * 删除对象
     */
    private void deleteObject(String bucketName, String objectName) throws Exception {
        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build());
    }

    /**
     * 从路径中提取文件名
     */
    private String extractFileName(String objectPath) {
        if (objectPath.contains("/")) {
            return objectPath.substring(objectPath.lastIndexOf("/") + 1);
        }
        return objectPath;
    }

    /**
     * description: 判断bucket是否存在，不存在则创建
     */
    public void existBucket(String bucketName) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs
                    .builder()
                    .bucket(bucketName)
                    .build());

            if (!exists) {
                makeBucket(bucketName);
                log.info("文件桶不存在， 新建桶成功 ！！");
            }
        } catch (Exception e) {
            throw new ServiceException("创建文件桶" + bucketName + "失败： " + e.getMessage());
        }
    }

    /**
     * 创建存储bucket
     *
     * @param bucketName 存储bucket名称
     */
    public void makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            throw new ServiceException("创建文件桶" + bucketName + "失败： " + e.getMessage());
        }
    }

    public List<MinioFileInfoVO> getAllFilesByBucketName(String bucketName) {
        existBucket(bucketName);

        List<MinioFileInfoVO> res = new ArrayList<>();
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).recursive(true).build()
        );

        results.forEach(r -> {
            MinioFileInfoVO info = new MinioFileInfoVO();
            try {
                Item item = r.get();
                info.setFileName(item.objectName());
                info.setLastModified(String.valueOf(item.lastModified()));
                res.add(info);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });

        return res;

    }

    /**
     * 获取指定文件夹下的所有文件和文件夹
     *
     * @param folderPath 文件夹路径，如 "" 表示根目录，"documents/" 或 "documents/subfolder/"
     * @return 包含文件和文件夹信息的列表
     */
    public List<FileObject> listFilesAndFolders(String bucketName, String folderPath) {
        existBucket(bucketName);

        List<FileObject> objects = new ArrayList<>();

        try {
            // 处理根目录情况
            boolean isRoot = StringUtils.isBlank(folderPath) || folderPath.equals("/");

            // 规范化路径 - 确保非根目录以"/"结尾
            String normalizedPath = isRoot ? "" :
                    folderPath.endsWith("/") ? folderPath : folderPath + "/";

            // 列出对象
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(bucketName)
                            .prefix(normalizedPath)
                            .delimiter("/")  // 使用分隔符来区分文件和文件夹
                            .recursive(false)
                            .build());

            // 处理文件和文件夹
            for (Result<Item> result : results) {
                Item item = result.get();

                // 跳过当前目录本身
                if (item.objectName().equals(normalizedPath)) {
                    continue;
                }

                FileObject fileObject = new FileObject();

                // 处理文件夹
                if (item.isDir()) {
                    String dirName = item.objectName().substring(normalizedPath.length());
                    dirName = dirName.endsWith("/") ? dirName.substring(0, dirName.length() - 1) : dirName;

                    fileObject.setName(dirName);
                    fileObject.setFolderFlag(true);
                    fileObject.setPath(item.objectName());
                }
                // 处理文件
                else {
                    String fileName = item.objectName().substring(normalizedPath.length());

                    // 跳过子目录中的文件（只处理当前目录下的直接文件）
                    if (fileName.contains("/")) {
                        continue;
                    }

                    fileObject.setName(fileName);
                    fileObject.setFolderFlag(false);
                    fileObject.setPath(item.objectName());
                    fileObject.setSize(item.size());
                    fileObject.setLastModified(Date.from(item.lastModified().toInstant()));
                }

                objects.add(fileObject);
            }
        } catch (Exception e) {
            throw new RuntimeException("Error listing files and folders from Minio", e);
        }

        return objects;
    }

    /**
     * 重命名文件
     *
     * @param oldObjectName 旧文件名（包含路径）
     * @param newObjectName 新文件名（包含路径）
     */
    public void renameFile(String bucketName, String oldObjectName, String newObjectName) throws Exception {
        // 检查旧文件是否存在
        if (!objectExists(bucketName, oldObjectName)) {
            throw new IllegalArgumentException("源文件不存在");
        }

        // 检查新文件名是否已存在
        if (objectExists(bucketName, newObjectName)) {
            throw new IllegalArgumentException("目标文件名已存在");
        }

        // 复制文件到新名称
        minioClient.copyObject(
                CopyObjectArgs.builder()
                        .bucket(bucketName)
                        .object(newObjectName)
                        .source(CopySource.builder()
                                .bucket(bucketName)
                                .object(oldObjectName)
                                .build())
                        .build());

        // 删除旧文件
        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(oldObjectName)
                        .build());
    }

    /**
     * 检查对象是否存在
     */
    public boolean objectExists(String bucketName, String objectName) throws Exception {
        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build());
            return true;
        } catch (ErrorResponseException e) {
            if (e.response().code() == 404) {
                return false;
            }
            throw e;
        }
    }

    /**
     * 重命名文件夹
     *
     * @param oldFolderPath 旧文件夹路径（以/结尾，如 "old-folder/"）
     * @param newFolderPath 新文件夹路径（以/结尾，如 "new-folder/"）
     */
    public void renameFolder(String bucketName, String oldFolderPath, String newFolderPath) throws Exception {
        // 确保路径以/结尾
        if (!oldFolderPath.endsWith("/")) oldFolderPath += "/";
        if (!newFolderPath.endsWith("/")) newFolderPath += "/";

        // 检查源文件夹是否存在
        if (!folderExists(bucketName, oldFolderPath)) {
            throw new IllegalArgumentException("源文件夹不存在");
        }

        // 检查目标文件夹是否已存在
        if (folderExists(bucketName, newFolderPath)) {
            throw new IllegalArgumentException("目标文件夹已存在");
        }

        // 列出文件夹下所有对象
        List<String> objectsToRename = new ArrayList<>();
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .prefix(oldFolderPath)
                        .recursive(true)
                        .build());

        for (Result<Item> result : results) {
            Item item = result.get();
            if (!item.isDir()) {
                objectsToRename.add(item.objectName());
            }
        }

        if (objectsToRename.isEmpty()) {
            throw new IllegalArgumentException("源文件夹为空");
        }

        // 逐个重命名
        for (String oldObjectName : objectsToRename) {
            String newObjectName = newFolderPath + oldObjectName.substring(oldFolderPath.length());
            renameFile(bucketName, oldObjectName, newObjectName);
        }
    }

    /**
     * 检查文件夹是否存在
     */
    private boolean folderExists(String bucketName, String folderPath) throws Exception {
        // 确保路径以/结尾
        if (!folderPath.endsWith("/")) folderPath += "/";

        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .prefix(folderPath)
                        .maxKeys(1)
                        .build());

        return results.iterator().hasNext();
    }

    /**
     * 读取纯文本文件，直接返回为 String
     */
    public String readTextFile(String bucketName, String objectName) throws Exception {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build())))) {
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
                // 添加系统相关的换行符以保持文件的原有格式
                stringBuilder.append(System.lineSeparator());
            }
            return stringBuilder.toString();
        }
    }


    //=============================== 大文件上传相关 -------------------------------------

    /**
     * 检查文件是否存在
     */
    public boolean fileExists(String bucketName, String objectName) {
        try {
            minioClient.statObject(StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());
            return true;
        } catch (ErrorResponseException e) {
            return false;
        } catch (Exception e) {
            throw new RuntimeException("检查文件是否存在失败", e);
        }
    }

    /**
     * 上传分片
     */
    public void uploadChunk(String bucketName, InputStream inputStream, long size, String objectName) {
        try {
            existBucket(bucketName);
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(inputStream, size, -1)
                    .contentType("application/octet-stream")
                    .build());
        } catch (Exception e) {
            throw new RuntimeException("上传分片失败", e);
        }
    }

    /**
     * 合并分片
     */
    public String mergeChunks(String bucketName, List<String> chunkObjectNames, String finalObjectName) {
        try {
            // 获取所有分片对象
            List<ComposeSource> sources = chunkObjectNames.stream()
                    .map(chunkObjectName -> ComposeSource.builder()
                            .bucket(bucketName)
                            .object(chunkObjectName)
                            .build())
                    .collect(Collectors.toList());

            // 合并分片
            minioClient.composeObject(ComposeObjectArgs.builder()
                    .bucket(bucketName)
                    .object(finalObjectName)
                    .sources(sources)
                    .build());

            // 删除临时分片文件
            for (String chunkObjectName : chunkObjectNames) {
                minioClient.removeObject(RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(chunkObjectName)
                        .build());
            }

            // 生成文件访问URL
            return getFileUrl(bucketName, finalObjectName);

        } catch (Exception e) {
            throw new RuntimeException("合并分片失败", e);
        }
    }

    /**
     * 获取文件URL
     */
    public String getFileUrl(String bucketName, String objectName) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .method(Method.GET)
                    .build());
        } catch (Exception e) {
            throw new RuntimeException("获取文件URL失败", e);
        }
    }

    /**
     * 删除文件
     */
    public void deleteFile(String bucketName, String objectName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build());
        } catch (Exception e) {
            throw new RuntimeException("删除文件失败", e);
        }
    }

    /**
     * 列出指定前缀的所有对象
     */
    public List<String> listObjects(String bucketName, String prefix) {
        List<String> objects = new ArrayList<>();
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
                    .bucket(bucketName)
                    .prefix(prefix)
                    .build());

            for (Result<Item> result : results) {
                Item item = result.get();
                objects.add(item.objectName());
            }
        } catch (Exception e) {
            throw new RuntimeException("列出对象失败", e);
        }
        return objects;
    }


    /**
     * 获取文件的 MD5（通过下载文件计算）
     */
    public String getFileMD5(String bucketName, String objectName) {
        try {
            // 获取文件流
            InputStream stream = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );

            // 计算 MD5
            return calculateMD5(stream);

        } catch (Exception e) {
            log.error("获取文件MD5失败: {}/{}", bucketName, objectName, e);
            throw new RuntimeException("获取文件MD5失败", e);
        }
    }

    /**
     * 分块计算大文件的 MD5（避免内存溢出）
     */
    public String getFileMD5Chunked(String bucketName, String objectName) {
        try {
            InputStream stream = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );

            return calculateMD5Chunked(stream);

        } catch (Exception e) {
            log.error("获取文件MD5失败: {}/{}", bucketName, objectName, e);
            throw new RuntimeException("获取文件MD5失败", e);
        }
    }

    /**
     * 计算输入流的 MD5
     */
    private String calculateMD5(InputStream inputStream) throws Exception {
        try (inputStream) {
            byte[] fileData = inputStream.readAllBytes();
            return DigestUtils.md5Hex(fileData);
        }
    }

    /**
     * 分块计算大文件的 MD5
     */
    private String calculateMD5Chunked(InputStream inputStream) throws Exception {
        try (inputStream) {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[8192]; // 8KB 缓冲区
            int bytesRead;

            while ((bytesRead = inputStream.read(buffer)) != -1) {
                digest.update(buffer, 0, bytesRead);
            }

            byte[] md5Bytes = digest.digest();
            return bytesToHex(md5Bytes);
        }
    }

    /**
     * 字节数组转十六进制字符串
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
    /**
     * MinIO对象信息类
     */
    @Data
    public static class MinioObject {
        private String objectName;
        private long size;
        private boolean isDirectory;
        private LocalDate lastModified;

    }
}


